package com.walter.common.utils;

import io.netty.channel.ChannelOption;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.*;
import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.resources.ConnectionProvider;

import java.io.File;
import java.time.Duration;

/**
 * WebClient 工具类，提供各种请求类型的便捷方法，包括文件上传。
 */
@Slf4j
public class WebClientUtil {

    private static final WebClient webClient;

    // 初始化 WebClient，可以进行全局配置，例如全局超时、编解码器等
    static {
        // 配置HTTP连接池
        ConnectionProvider provider = ConnectionProvider.builder("custom") // 自定义连接池名称
                .maxConnections(500) // 最大连接数
                .maxIdleTime(Duration.ofSeconds(20)) // 空闲连接超时时间
                .build();

        // 配置HTTP客户端
        HttpClient httpClient = HttpClient.create(provider)
                .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) // 连接超时时间
                .responseTimeout(Duration.ofSeconds(5)) // 响应超时时间
                .doOnConnected(conn ->
                        conn.addHandlerLast(new ReadTimeoutHandler(5)) // 读取超时时间
                                .addHandlerLast(new WriteTimeoutHandler(5))); // 写入超时时间
        // 设置内存限制，处理文件上传等场景
        ExchangeStrategies strategies = ExchangeStrategies.builder()
                .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2 * 1024 * 1024)) // 设置更大的内存限制，处理文件上传等场景
                .build();
        webClient = WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .exchangeStrategies(strategies)
                // .baseUrl("http://localhost:8080") // 可设置全局baseUrl，如果所有请求都基于同一个基础URL
                .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) // 设置默认请求头
                .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) // 设置默认的响应头
                // 添加请求日志记录功能
                .filter(ExchangeFilterFunction.ofRequestProcessor(
                        clientRequest -> {
                            log.debug("Request: {} {}",
                                    clientRequest.method(),
                                    clientRequest.url());
                            return Mono.just(clientRequest);
                        }
                ))
                // 添加响应日志记录功能
                .filter(ExchangeFilterFunction.ofResponseProcessor(
                        clientResponse -> {
                            log.debug("Response status: {}",
                                    clientResponse.statusCode());
                            return Mono.just(clientResponse);
                        }
                ))
                .build();
    }

    /**
     * GET 请求
     *
     * @param url          请求URL
     * @param responseType 响应类型
     * @param headers  自定义请求头 (可选)
     * @param <T>          响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> get(String url, Class<T> responseType, HttpHeaders headers) {
        return exchange(HttpMethod.GET, url, responseType,headers);
    }

    public static <T> T getBlock(String url, Class<T> responseType, HttpHeaders headers) {
        return get(url, responseType, headers).block();
    }
    public static String getBlockString(String url, HttpHeaders headers) {
        return get(url, String.class , headers).block();
    }
    /**
     * GET 请求 (无自定义请求头)
     *
     * @param url          请求URL
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> get(String url, Class<T> responseType) {
        return get(url, responseType, null);
    }


    /**
     * POST 请求，RequestBody 为 JSON 或其他可被 Jackson 序列化的对象
     *
     * @param url          请求URL
     * @param requestBody  请求体对象
     * @param responseType 响应类型
     * @param headers  自定义请求头 (可选)
     * @param <T>          响应类型泛型
     * @param <B>          请求体类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T, B> Mono<T> post(String url, B requestBody, Class<T> responseType, HttpHeaders headers) {
        return exchange(HttpMethod.POST, url, responseType, requestBody,headers);
    }

    public static <T, B> T postBlock(String url, B requestBody, Class<T> responseType, HttpHeaders headers) {
        return post(url, requestBody, responseType,headers).block();
    }

    /**
     * POST 请求，RequestBody 为 JSON 或其他可被 Jackson 序列化的对象 (无自定义请求头)
     *
     * @param url          请求URL
     * @param requestBody  请求体对象
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @param <B>          请求体类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T, B> Mono<T> post(String url, B requestBody, Class<T> responseType) {
        return post(url, requestBody, responseType, null);
    }


    /**
     * POST 请求，RequestBody 为 Multipart/form-data，用于文件上传等场景
     *
     * @param url                 请求URL
     * @param multipartBodyBuilder MultipartBodyBuilder 构建器，用于添加文件和表单字段
     * @param responseType        响应类型
     * @param headers     自定义请求头 (可选)
     * @param <T>                 响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> post(String url, MultipartBodyBuilder multipartBodyBuilder, Class<T> responseType, HttpHeaders headers) {
        return exchange(HttpMethod.POST, url, responseType, multipartBodyBuilder,headers);
    }

    /**
     * POST 请求，RequestBody 为 Multipart/form-data，用于文件上传等场景 (无自定义请求头)
     *
     * @param url                 请求URL
     * @param filePath MultipartBodyBuilder 构建器，用于添加文件和表单字段
     * @param responseType        响应类型
     * @param <T>                 响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> postFile(String url, String filePath, String fileName, Class<T> responseType) {
        File file = new File(filePath);
        // 示例 POST 请求 (Multipart/form-data with file)
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
        multipartBodyBuilder.part(fileName, file)
                .filename(fileName); // 可选，设置文件名

        return post(url, multipartBodyBuilder, responseType, null);
    }


    /**
     * PUT 请求
     *
     * @param url          请求URL
     * @param requestBody  请求体对象
     * @param responseType 响应类型
     * @param headers  自定义请求头 (可选)
     * @param <T>          响应类型泛型
     * @param <B>          请求体类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T, B> Mono<T> put(String url, B requestBody, Class<T> responseType, HttpHeaders headers) {
        return exchange(HttpMethod.PUT, url, responseType, requestBody,headers);
    }

    /**
     * PUT 请求 (无自定义请求头)
     *
     * @param url          请求URL
     * @param requestBody  请求体对象
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @param <B>          请求体类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T, B> Mono<T> put(String url, B requestBody, Class<T> responseType) {
        return put(url, requestBody, responseType, null);
    }


    /**
     * DELETE 请求
     *
     * @param url          请求URL
     * @param responseType 响应类型
     * @param headers  自定义请求头 (可选)
     * @param <T>          响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> delete(String url, Class<T> responseType, HttpHeaders headers) {
        return exchange(HttpMethod.DELETE, url, responseType,headers);
    }

    /**
     * DELETE 请求 (无自定义请求头)
     *
     * @param url          请求URL
     * @param responseType 响应类型
     * @param <T>          响应类型泛型
     * @return Mono<T> 响应结果
     */
    public static <T> Mono<T> delete(String url, Class<T> responseType) {
        return delete(url, responseType, null);
    }

    private static <T> Mono<T> exchange(HttpMethod httpMethod, String url, Class<T> responseType, HttpHeaders headers) {
        return exchange(httpMethod, url, responseType, null, headers);
    }

    private static <T> Mono<T> exchange(HttpMethod httpMethod, String url, Class<T> responseType, Object body) {
        return exchange(httpMethod, url, responseType, body, null);
    }

    private static <T> Mono<T> exchange(HttpMethod httpMethod, String url, Class<T> responseType) {
        return exchange(httpMethod, url, responseType, null, null);
    }
    private static <T> Mono<T> exchange(HttpMethod httpMethod, String url, Class<T> responseType, Object body, HttpHeaders headers) {
        long startTime = System.currentTimeMillis();

        WebClient.RequestBodySpec requestBodySpec = webClient.method(httpMethod)
                .uri(url)
                .headers(h -> {
                    if (headers != null) {
                        h.addAll(headers);
                    }
                });

        WebClient.RequestHeadersSpec<?> requestHeadersSpec;

        if (body != null) {
            requestHeadersSpec = requestBodySpec
                    .contentType(MediaType.APPLICATION_JSON) // 默认发送JSON
                    .bodyValue(body);
        } else {
            requestHeadersSpec = requestBodySpec;
        }

        return requestHeadersSpec
                .accept(MediaType.APPLICATION_JSON) // 默认接受JSON
                .exchangeToMono(response -> {
                    if (!response.statusCode().is2xxSuccessful()) {// 处理非2xx状态码
                        long endTime = System.currentTimeMillis();
                        // 可以根据需要处理错误响应，例如抛出异常或者返回默认值
                        log.error("请求 {} {} 失败，状态码: {}，耗时: {} ms", httpMethod, url, response.statusCode(), endTime - startTime);
                        return Mono.error(new RuntimeException("请求失败，状态码: " + response.statusCode())); // 抛出异常
                        // 或者返回默认值: return Mono.empty();
                    } else {
                        long endTime = System.currentTimeMillis();
                        log.info("请求 {} {} 成功，耗时: {} ms", httpMethod, url, endTime - startTime);
                        return response.bodyToMono(responseType);
                    }
                });
    }

    /**
     * 统一错误处理
     *
     * @param clientResponse ClientResponse
     * @return Mono<Throwable>
     */
    private static Mono<? extends Throwable> handleError(ClientResponse clientResponse) {
        // 可以根据状态码和响应体进行更详细的错误处理
        return clientResponse.bodyToMono(String.class)
                .flatMap(errorBody -> Mono.error(new RuntimeException("请求失败: " + clientResponse.statusCode() + ", 响应内容: " + errorBody)));
    }

    public static void main(String[] args) {
        // 示例 GET 请求
        Mono<String> getResponse = WebClientUtil.get("https://apis.tianapi.com/wxhottopic/index?key=4fec1b82f85a15853a7085d0d75be60c", String.class);
        //异步获取结果
        getResponse.subscribe(System.out::println, error -> System.err.println("GET 请求失败: " + error.getMessage()));

        // 示例 POST 请求 (JSON body)
        /*Mono<String> postResponseJson = WebClientUtil.post("https://httpbin.org/post", java.util.Collections.singletonMap("key", "value"), String.class);
        postResponseJson.subscribe(System.out::println, error -> System.err.println("POST JSON 请求失败: " + error.getMessage()));

        // 示例 POST 请求 (Multipart/form-data with file)
        MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
        multipartBodyBuilder.part("textParam", "Hello Multipart");
        // 添加文件，Resource 可以是 FileSystemResource, ClassPathResource, UrlResource 等
        // 假设 resources 目录下有一个 example.txt 文件
        Resource fileResource = new org.springframework.core.io.ClassPathResource("example.txt");
        multipartBodyBuilder.part("fileParam", fileResource)
                .filename("example.txt") // 可选，设置文件名
                .contentType(MediaType.TEXT_PLAIN); // 可选，设置 Content-Type

        Mono<String> postResponseFile = WebClientUtil.post("https://httpbin.org/post", multipartBodyBuilder, String.class);
        postResponseFile.subscribe(System.out::println, error -> System.err.println("POST File 请求失败: " + error.getMessage()));*/


        // 注意: main 方法的示例是异步的，为了方便查看结果，可以添加 Thread.sleep() 等待一段时间，
        // 但在实际 WebFlux 应用中，应该使用非阻塞的方式处理 Mono 和 Flux。
        try {
            Thread.sleep(5000); // 等待一段时间查看结果
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

